/* 
 * File:   RegularGrid.cpp
 * Author: walther
 * 
 * Created on October 20, 2012, 8:39 PM
 */

#include <omp.h>
#include <stdio.h>
#include "RegularGrid.h"
#include "Grid.h"
#include "RegularGridIterator.h"

RegularGrid::RegularGrid( std::vector<Object*>& objects, int nX, int nY, int nZ ) : Grid( objects )
{
    _cellNumber[0] = nX;
    _cellNumber[1] = nY;
    _cellNumber[2] = nZ;
    
    buildGrid( objects );
}



RegularGrid::~RegularGrid()
{
    //TODO
}



GridIterator* RegularGrid::getIterator( Ray& ray )
{
    RegularGridIterator* iterator = new RegularGridIterator( this, ray );
    
    return iterator;
}



GridCell* RegularGrid::getCell( int x, int y, int z )
{
    int index = z + y*_cellNumber[2] + x*_cellNumber[1]*_cellNumber[2];
    return _cells[index];
}



void RegularGrid::getCellIndex( Vector4D point, int& x, int& y, int& z )
{
    Vector4D boxOrigin = _boundingBox->getMin();
    
    x = (int) (( point.x - boxOrigin.x ) / _cellSizes.x);
    y = (int) (( point.y - boxOrigin.y ) / _cellSizes.y);
    z = (int) (( point.z - boxOrigin.z ) / _cellSizes.z);
}



Vector4D RegularGrid::getCellSizes()
{
    return _cellSizes;
}



void RegularGrid::buildGrid( std::vector<Object*>& objects )
{
    printf("Building grid...\n");
    
    double time0 = omp_get_wtime();
    
    // Compute the size of each cell
    Vector4D boxMin   = _boundingBox->getMin();
    Vector4D boxMax   = _boundingBox->getMax();
    _cellSizes = boxMax - boxMin;
    _cellSizes.x /= _cellNumber[0];
    _cellSizes.y /= _cellNumber[1];
    _cellSizes.z /= _cellNumber[2];
    
    // Fill the cell array
    for (int cx = 0; cx < _cellNumber[0]; ++cx)
    {
        for (int cy = 0; cy < _cellNumber[1]; ++cy)
        {
            for (int cz = 0; cz < _cellNumber[2]; ++cz)
            {
                Vector4D cellMin( boxMin.x + cx       * _cellSizes.x, boxMin.y + cy       * _cellSizes.y, boxMin.z + cz       * _cellSizes.z, 1.0 );
                Vector4D cellMax( boxMin.x + (cx + 1) * _cellSizes.x, boxMin.y + (cy + 1) * _cellSizes.y, boxMin.z + (cz + 1) * _cellSizes.z, 1.0 );
                
                _cells.push_back( new GridCell( cellMin, cellMax ) );
            }
        }
    }
    
    for (int objID = 0; objID < objects.size(); ++objID)
    {
        // Get the Bounding box of in objects
        Vector4D objMin, objMax;
        objects[objID]->getBoundingBox( objMin, objMax );
        
        // Find the lowest cell that the box overlaps
        int cellXMin, cellYMin, cellZMin;
        getCellIndex( objMin, cellXMin, cellYMin, cellZMin );
        
        // Find the Highest cell that the box overlaps
        int cellXMax, cellYMax, cellZMax;
        getCellIndex( objMax, cellXMax, cellYMax, cellZMax );
        
        // Loop through the cells between the lowest and highest overlapping cells           
        for (int xid = cellXMin; xid <= cellXMax; ++xid)
        {
            for (int yid = cellYMin; yid <= cellYMax; ++yid)
            {
                for (int zid = cellZMin; zid <= cellZMax; ++zid)
                {
                    int index = zid + yid*_cellNumber[2] + xid*_cellNumber[1]*_cellNumber[2];
                    _cells[index]->addObject( objID );
                }
            }
        }
    }
    
    printf("Grid constructed in %lf seconds\n", omp_get_wtime() - time0 );
}



void RegularGrid::getCellNumber( int& nx, int& ny, int& nz )
{
    nx = _cellNumber[0];
    ny = _cellNumber[1];
    nz = _cellNumber[2];
}
